/**
 * 测试工具类
 * 提供单元测试的基础框架和断言方法
 */

/**
 * 测试结果类型
 */
export interface TestResult {
  testName: string;
  passed: boolean;
  message: string;
  duration: number;
}

/**
 * 测试套件
 */
export interface TestSuite {
  name: string;
  tests: TestResult[];
  passedCount: number;
  failedCount: number;
  duration: number;
}

/**
 * 测试断言工具类
 */
export class Assert {
  /**
   * 断言值为真
   * @param condition 条件
   * @param message 错误消息
   */
  static isTrue(condition: boolean, message?: string): void {
    if (!condition) {
      throw new Error(message || '断言失败: 期望为真，但实际为假');
    }
  }

  /**
   * 断言值为假
   * @param condition 条件
   * @param message 错误消息
   */
  static isFalse(condition: boolean, message?: string): void {
    if (condition) {
      throw new Error(message || '断言失败: 期望为假，但实际为真');
    }
  }

  /**
   * 断言值相等
   * @param actual 实际值
   * @param expected 期望值
   * @param message 错误消息
   */
  static equals<T>(actual: T, expected: T, message?: string): void {
    if (actual !== expected) {
      throw new Error(message || `断言失败: 期望 ${expected}，但实际 ${actual}`);
    }
  }

  /**
   * 断言值不相等
   * @param actual 实际值
   * @param expected 期望值
   * @param message 错误消息
   */
  static notEquals<T>(actual: T, expected: T, message?: string): void {
    if (actual === expected) {
      throw new Error(message || `断言失败: 期望不等于 ${expected}，但实际相等`);
    }
  }

  /**
   * 断言值不为空/undefined/null
   * @param value 值
   * @param message 错误消息
   */
  static notNull(value: any, message?: string): void {
    if (value === null || value === undefined) {
      throw new Error(message || '断言失败: 期望值不为空，但实际为空');
    }
  }

  /**
   * 断言值为空/undefined/null
   * @param value 值
   * @param message 错误消息
   */
  static isNull(value: any, message?: string): void {
    if (value !== null && value !== undefined) {
      throw new Error(message || '断言失败: 期望值为空，但实际不为空');
    }
  }

  /**
   * 断言数组包含元素
   * @param array 数组
   * @param element 元素
   * @param message 错误消息
   */
  static contains<T>(array: T[], element: T, message?: string): void {
    if (!array.includes(element)) {
      throw new Error(message || `断言失败: 数组不包含元素 ${element}`);
    }
  }

  /**
   * 断言数组不包含元素
   * @param array 数组
   * @param element 元素
   * @param message 错误消息
   */
  static notContains<T>(array: T[], element: T, message?: string): void {
    if (array.includes(element)) {
      throw new Error(message || `断言失败: 数组包含元素 ${element}`);
    }
  }

  /**
   * 断言对象包含属性
   * @param obj 对象
   * @param property 属性名
   * @param message 错误消息
   */
  static hasProperty(obj: object, property: string, message?: string): void {
    if (!(property in obj)) {
      throw new Error(message || `断言失败: 对象不包含属性 ${property}`);
    }
  }

  /**
   * 断言函数抛出异常
   * @param fn 函数
   * @param message 错误消息
   */
  static throws(fn: () => void, message?: string): void {
    let threw = false;
    try {
      fn();
    } catch (e) {
      threw = true;
    }

    if (!threw) {
      throw new Error(message || '断言失败: 期望函数抛出异常，但实际没有');
    }
  }
}

/**
 * 测试运行器
 */
export class TestRunner {
  private static testSuites: TestSuite[] = [];

  /**
   * 创建测试套件
   * @param name 套件名称
   * @param testFn 测试函数
   */
  static describe(name: string, testFn: () => void): void {
    const suite: TestSuite = {
      name,
      tests: [],
      passedCount: 0,
      failedCount: 0,
      duration: 0
    };

    // 临时存储当前套件，供it方法使用
    TestRunner.currentSuite = suite;

    // 执行测试函数，注册测试用例
    testFn();

    // 计算测试结果
    suite.passedCount = suite.tests.filter(t => t.passed).length;
    suite.failedCount = suite.tests.filter(t => !t.passed).length;
    suite.duration = suite.tests.reduce((sum, t) => sum + t.duration, 0);

    // 添加到套件列表
    TestRunner.testSuites.push(suite);
  }

  // 当前正在执行的测试套件
  private static currentSuite: TestSuite | null = null;

  /**
   * 注册测试用例
   * @param name 测试名称
   * @param testFn 测试函数
   */
  static it(name: string, testFn: () => void | Promise<void>): void {
    if (!TestRunner.currentSuite) {
      throw new Error('测试用例必须在describe块内定义');
    }

    const startTime = Date.now();
    let passed = false;
    let message = '';

    try {
      const result = testFn();
      
      // 处理异步测试
      if (result instanceof Promise) {
        result.then(() => {
          passed = true;
        }).catch((e) => {
          passed = false;
          message = e.message || '测试执行失败';
        });
      } else {
        passed = true;
      }
    } catch (e) {
      passed = false;
      message = e.message || '测试执行失败';
    }

    const duration = Date.now() - startTime;

    const testResult: TestResult = {
      testName: name,
      passed,
      message,
      duration
    };

    TestRunner.currentSuite.tests.push(testResult);
  }

  /**
   * 运行所有测试
   * @returns 测试结果
   */
  static async runAllTests(): Promise<TestSuite[]> {
    console.log('开始运行测试...\n');
    
    for (const suite of TestRunner.testSuites) {
      console.log(`测试套件: ${suite.name}`);
      
      for (const test of suite.tests) {
        const status = test.passed ? '✓' : '✗';
        console.log(`  ${status} ${test.testName} (${test.duration}ms)`);
        
        if (!test.passed) {
          console.log(`    错误: ${test.message}`);
        }
      }
      
      console.log(`  通过: ${suite.passedCount}, 失败: ${suite.failedCount}, 耗时: ${suite.duration}ms\n`);
    }
    
    const totalPassed = TestRunner.testSuites.reduce((sum, s) => sum + s.passedCount, 0);
    const totalFailed = TestRunner.testSuites.reduce((sum, s) => sum + s.failedCount, 0);
    const totalDuration = TestRunner.testSuites.reduce((sum, s) => sum + s.duration, 0);
    
    console.log(`总计: 通过 ${totalPassed}, 失败 ${totalFailed}, 耗时 ${totalDuration}ms`);
    
    return TestRunner.testSuites;
  }

  /**
   * 清除所有测试套件
   */
  static clear(): void {
    TestRunner.testSuites = [];
  }
}

/**
 * 模拟函数
 */
export class Mock<T = any> {
  private calls: Array<any[]> = [];
  private returnValues: any[] = [];
  private returnValue: any;

  /**
   * 创建模拟函数
   * @param impl 可选的实现函数
   */
  constructor(impl?: (...args: any[]) => any) {
    if (impl) {
      this.returnValue = impl;
    }
  }

  /**
   * 执行模拟函数
   * @param args 参数
   * @returns 返回值
   */
  mockFn(...args: any[]): any {
    this.calls.push(args);
    
    if (this.returnValues.length > 0) {
      return this.returnValues.shift();
    }
    
    if (this.returnValue !== undefined) {
      return typeof this.returnValue === 'function' ? this.returnValue(...args) : this.returnValue;
    }
    
    return undefined;
  }

  /**
   * 设置返回值
   * @param value 返回值
   */
  returns(value: any): Mock<T> {
    this.returnValue = value;
    return this;
  }

  /**
   * 设置多个返回值序列
   * @param values 返回值序列
   */
  returnsSequence(...values: any[]): Mock<T> {
    this.returnValues = [...values];
    return this;
  }

  /**
   * 获取调用次数
   */
  get callCount(): number {
    return this.calls.length;
  }

  /**
   * 获取所有调用参数
   */
  get callsWithArgs(): any[][] {
    return [...this.calls];
  }

  /**
   * 获取最后一次调用的参数
   */
  get lastCall(): any[] {
    return this.calls.length > 0 ? this.calls[this.calls.length - 1] : [];
  }

  /**
   * 验证是否被调用
   */
  wasCalled(): boolean {
    return this.calls.length > 0;
  }

  /**
   * 验证是否被指定参数调用
   * @param args 参数
   */
  wasCalledWith(...args: any[]): boolean {
    return this.calls.some(call => 
      call.length === args.length && call.every((arg, index) => arg === args[index])
    );
  }

  /**
   * 重置模拟状态
   */
  reset(): void {
    this.calls = [];
    this.returnValues = [];
    this.returnValue = undefined;
  }
}

/**
 * 创建模拟函数
 * @param impl 可选的实现函数
 * @returns 模拟函数
 */
export function mockFn<T = any>(impl?: (...args: any[]) => any): Mock<T> {
  return new Mock<T>(impl);
}